﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.ComponentModel;
using System.IO;

namespace VKontakteClient
{
    /// <summary>
    /// Класс реализующий кэширующее скачивание файлов
    /// </summary>
    public class Downloadable : INotifyPropertyChanged
    {
        /// <summary>
        /// Перегруженное получение хэшкода
        /// </summary>
        public override int GetHashCode()
        {
            return InternetUri.GetHashCode();
        }

        /// <summary>
        /// Перегруженный метод сравнения
        /// </summary>
        public override bool Equals(object obj)
        {
            var X = obj as Downloadable;
            if (X == null) return false;
            return X.InternetUri == this.InternetUri;
        }

        /// <summary>
        /// Интернетовская ссылка
        /// </summary>
        private Uri _internetUri;
        public Uri InternetUri
        {
            get
            {
                return _internetUri;
            }
            //set
            //{
            //    _internetUri = value;
            //}
        }

        /// <summary>
        /// Реальное имя локального файла
        /// </summary>
        private string _localFileName = null;

        /// <summary>
        /// Имя локального файла
        /// </summary>
        public string LocalFileName
        {
            get
            {
                if (File.Exists(_localFileName)) return _localFileName;
                return null;
            }
            set
            {
                _localFileName = value;
            }
        }

        /// <summary>
        /// Показывает не скачивается ли уже файл
        /// </summary>
        private bool _downloading = false;

        /// <summary>
        /// Сколько байт получено, если скачивается
        /// </summary>
        private long _bytesReceived;

        /// <summary>
        /// Сколько байт надо получить, если скачивается
        /// </summary>
        private long _bytesTotal;

        /// <summary>
        /// Жив ли поток скачивания
        /// </summary>
        private bool _finished;

        /// <summary>
        /// Перегруженный метод преобразования в строку, показывает статус(кэшировано, или скачивается)
        /// </summary>
        public override string ToString()
        {
            if (LocalFileName != null) return "Кэшировано";
            else if (_downloading)
            {
                if (_bytesTotal != 0)
                    return "Скачивается... Готово " + SizeToText(_bytesReceived) + " из " + SizeToText(_bytesTotal);
                else
                    return "Скачивается...";
            }
            return "Не кэшировано";
        }

        /// <summary>
        /// Преобразование кол-ва байт в кол-во килобайт
        /// </summary>
        /// <param name="l">Входное количество байт</param>
        /// <returns>Возвращается строка - в байтах или килобайтах</returns>
        private static string SizeToText(long l)
        {
            if (l < 1024) return l + "б";
            l /= 1024; return l + "Кб";
        }

        /// <summary>
        /// Приватный объект для межпоточных блокировок
        /// </summary>
        private object _lockobj = new object();

        /// <summary>
        /// Ссылка на файл в локальном кэше
        /// </summary>
        public Uri Uri
        {
            get
            {
                if (LocalFileName != null && File.Exists(LocalFileName))
                {
                    var path = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), LocalFileName);
                    Uri u = new Uri(path, UriKind.Absolute);
                    return u;
                }
                try
                {
                    if (App.IsOffline) return null;
                    lock(_lockobj)
                    {
                        if (!_downloading)
                        {
                            _downloading = true;
                            App.Scheduler.Enqueue(() =>
                            {
                                var path = "Cache/" + Guid.NewGuid().ToString().Replace("{", "").Replace("}", "") + System.IO.Path.GetExtension(InternetUri.AbsolutePath);
                                if (!System.IO.Directory.Exists("Cache"))
                                    System.IO.Directory.CreateDirectory("Cache");
                                WebClient wc = new WebClient();
                                try
                                {
                                    wc.DownloadProgressChanged += (object o, DownloadProgressChangedEventArgs e) =>
                                    {
                                        _bytesReceived = e.BytesReceived;
                                        _bytesTotal = e.TotalBytesToReceive;
                                        _finished = false;
                                    };
                                    wc.DownloadFileCompleted += (object o, AsyncCompletedEventArgs e) =>
                                    {
                                        _finished = true;
                                        if (!e.Cancelled && e.Error == null)
                                        {
                                            App.SqlCeAPI.UpdateDownloadable(InternetUri.AbsoluteUri, path);
                                            LocalFileName = path;
                                        }
                                    };
                                    wc.DownloadFileAsync(InternetUri, path);
                                    while (!_finished) System.Threading.Thread.Sleep(50);
                                }
                                catch (Exception e)
                                {
                                    DebugLog.DebugException(e);
                                }
                            }, System.Threading.ThreadPriority.Normal, () =>
                            {
                                return "Получение файла..." + this.ToString();
                            }, () =>
                            {
                                if (PropertyChanged != null)
                                    PropertyChanged(this, new PropertyChangedEventArgs("Uri"));
                                _downloading = false;
                            });
                        }
                    }
                    return null;
                }
                catch
                {
                    return InternetUri;
                }
            }
            set
            {
                if (_internetUri != value)
                {
                    _internetUri = value;
                    if (_internetUri != null)
                        LocalFileName = App.SqlCeAPI.GetDownloadableLocalFileNameByUrl(_internetUri.AbsoluteUri);
                }
            }
        }

        /// <summary>
        /// Событие что изменилось свойство
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
    }
}
